#include <shader.h>

void DoNothingShader::VertexShader(Vertex& vert1, Vertex& vert2, Vertex& vert3) {
    vert1.position = mProj * mView * mModel * vert1.position;
    vert2.position = mProj * mView * mModel * vert2.position;
    vert3.position = mProj * mView * mModel * vert3.position;
}

Color4u8 DoNothingShader::FragmentShader(float alpha, float beta, float gamma) {
    return color;
}

void PhongShader::VertexShader(Vertex& vert1, Vertex& vert2, Vertex& vert3) {
    mTriPos[0] = mModel * vert1.position;
    mTriPos[1] = mModel * vert2.position;
    mTriPos[2] = mModel * vert3.position;

    mTriNormal[0] = mModel * vert1.normal;
    mTriNormal[1] = mModel * vert2.normal;
    mTriNormal[2] = mModel * vert3.normal;

    mTriTexCoord[0] = vert1.texCoord;
    mTriTexCoord[1] = vert2.texCoord;
    mTriTexCoord[2] = vert3.texCoord;

    vert1.position = mProj * mView * mModel * vert1.position;
    vert2.position = mProj * mView * mModel * vert2.position;
    vert3.position = mProj * mView * mModel * vert3.position;
}

Color4u8 PhongShader::FragmentShader(float alpha, float beta, float gamma) {
    Vec3f    fragPos    = mTriPos[0] *              alpha + mTriPos[1] *      beta + mTriPos[2] *      gamma;
    Vec3f    fragNormal = normalize(mTriNormal[0] * alpha + mTriNormal[1] *   beta + mTriNormal[2] *   gamma);
    TexCoord fragUV     = mTriTexCoord[0] *         alpha + mTriTexCoord[1] * beta + mTriTexCoord[2] * gamma;
    Color4f albedo = mTexture.simple(fragUV.u, fragUV.v) / 255.0f;
    Color4u8 resultColor{};

    for (const auto& light : mLights) {
        auto [lightDir, lightColor] = light->getLightInfo(fragPos);
        Vec3f viewDir = normalize(mViewPos - fragPos);
        Vec3f reflectDir = lightDir - 2.0f * (fragNormal * lightDir) * fragNormal;
        
        float ambientStrength = 0.1f;
        Color4f ambient = ambientStrength * colorMix(lightColor, albedo);
        
        Color4f diffuse = std::max(fragNormal * lightDir, 0.0f) * colorMix(albedo, lightColor);
        float specularStrength = 0.7f;
        float spec = std::powf(std::max(viewDir * reflectDir, 0.0f), 32.0f);
        Color4f specular = specularStrength * spec * lightColor;

        resultColor = resultColor + vector_cast<std::uint8_t>((ambient + diffuse + specular) * 255.0f);
    }

    return resultColor;
}